package com.example.lotterysystem.service.impl;

import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.RedisUtil;
import com.example.lotterysystem.controller.param.DrawPrizeParam;
import com.example.lotterysystem.controller.param.ShowWinningResultParam;
import com.example.lotterysystem.dao.dataobject.*;
import com.example.lotterysystem.dao.mapper.*;
import com.example.lotterysystem.service.DrawPrizeService;
import com.example.lotterysystem.service.dto.ShowWinningResultDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.example.lotterysystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.example.lotterysystem.common.config.DirectRabbitConfig.ROUTING;
@Service
@Slf4j
public class DrawPrizeServiceImpl implements DrawPrizeService {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private WinnerRecordMapper winnerRecordMapper;
    @Autowired
    private RedisUtil redisUtil;

    public static final Long WINNING_RECORDS_TIMEOUT = 60 * 60 * 24 * 3L; //缓存时间
    public static final String WINNING_RECORDS_PREFIX = "WINNING_RECORDS_";


    /**
     * 校验抽奖请求信息
     * @param param
     */
    @Override
    public Boolean checkDrawPrizeValid(DrawPrizeParam param) {
        // 校验活动是否存在
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        // 校验奖品是否存在
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());
        if (null == activityDO || null == activityPrizeDO) {
           // throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_NOT_EXIST);
            log.info("校验抽奖请求失败！失败原因:{}",
                    ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_NOT_EXIST.getMessage());
            return false;
        }
        // 校验活动是否有效
        if (ActivityStatusEnum.COMPLETED.name().equalsIgnoreCase(activityDO.getStatus())) {
          //  throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_COMPLETED);
            log.info("校验抽奖请求失败！失败原因:{}",
                    ServiceErrorCodeConstants.ACTIVITY_COMPLETED.getMessage());
            return false;
        }
        // 校验奖品是否有效
        if (ActivityPrizeStatusEnum.COMPLETED.name().equalsIgnoreCase(activityPrizeDO.getPrizeTiers())) {
//            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED);
            log.info("校验抽奖请求失败！失败原因:{}",
                    ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED.getMessage());
            return false;
        }
        // 校验中奖人数是否与奖品数量一致
        if (activityPrizeDO.getPrizeAmount() != param.getWinnerList().size()) {
//            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_COUNT_ERROR);
            log.info("校验抽奖请求失败！失败原因:{}",
                    ServiceErrorCodeConstants.ACTIVITY_PRIZE_COUNT_ERROR.getMessage());
            return false;
        }
        // 校验完成，抽奖请求符合要求
        return true;
    }

    /**
     * 查询中奖记录结果
     * @param param
     * @return
     */
    @Override
    public List<ShowWinningResultDTO> showWinningResult(ShowWinningResultParam param) {
        // 查询 redis 中保存的活动、奖品信息
        String key = (null == param.getPrizeId()
                ? String.valueOf(param.getActivityId())
                : param.getActivityId() + "_" + param.getPrizeId());
        List<WinningRecordDO> winningRecordDOList = getWinningRecords(key);
        if (!CollectionUtils.isEmpty(winningRecordDOList)) {
            return convertToWinningRecordDTOList(winningRecordDOList);
        }
        // 未在 redis 中查询到相关记录
        // 继续在数据库中进行查询
        winningRecordDOList = winnerRecordMapper.selectByActivityIdOrPrizeId(
                param.getActivityId(), param.getPrizeId());
        // 将记录存放到 redis 中
        if (CollectionUtils.isEmpty(winningRecordDOList)) {
            log.info("查询的中奖记录为空！param: {}",
                    JacksonUtil.writeValueAsString(winningRecordDOList));
            return Arrays.asList();
        }
        cacheWinningRecords(key, winningRecordDOList, WINNING_RECORDS_TIMEOUT);
        return convertToWinningRecordDTOList(winningRecordDOList);
    }

    /**
     * 从 redis 中获取中奖记录
     * @param key
     * @return
     */
    private List<WinningRecordDO> getWinningRecords(String key) {
        try {
            if (!StringUtils.hasText(key)) {
                log.warn("要从缓存中查询的中奖记录的 key 为空！");
                return Arrays.asList();
            }
            String str = redisUtil.get(WINNING_RECORDS_PREFIX + key);
            if (!StringUtils.hasText(str)) {
                return Arrays.asList();
            }
            return JacksonUtil.readListValue(str, WinningRecordDO.class);
        } catch (Exception e) {
            log.warn("从缓存中查询中奖记录异常！key: {}",
                    WINNING_RECORDS_PREFIX + key, e);
            return Arrays.asList();
        }
    }

    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        if (null == activityId) {
            log.warn("要删除的中奖记录活动 id 为空");
            return;
        }
        // 删除数据库中的数据
        winnerRecordMapper.deleteRecord(activityId,
                prizeId);
        // 删除缓存中的数据
        if (null != prizeId) {
            deleteCacheWinningRecords(activityId + "_" + prizeId);
        }
        if (null != getWinningRecords(String.valueOf(activityId))) {
            deleteCacheWinningRecords(String.valueOf(activityId));
        }
    }

    /**
     * 删除缓存中的中奖记录
     * @param key
     */
    private void deleteCacheWinningRecords(String key) {
        if (null == key) {
            log.warn("需删除缓存的 key 为空");
            return;
        }
        redisUtil.del(WINNING_RECORDS_PREFIX + key);
    }

    /**
     * 将 WinningRecordDO 类型转化为 ShowWinningResultDTO 类型列表
     * @param winningRecordDOList
     * @return
     */
    private List<ShowWinningResultDTO> convertToWinningRecordDTOList(List<WinningRecordDO> winningRecordDOList) {
        if (CollectionUtils.isEmpty(winningRecordDOList)) {
            return Arrays.asList();
        }
        List<ShowWinningResultDTO> showWinningResultDTOList = winningRecordDOList.stream()
                .map(winningRecordDO -> {
                    ShowWinningResultDTO showWinningResultDTO = new ShowWinningResultDTO();
                    showWinningResultDTO.setWinnerId(winningRecordDO.getWinnerId());
                    showWinningResultDTO.setWinnerName(winningRecordDO.getWinnerName());
                    showWinningResultDTO.setPrizeName(winningRecordDO.getPrizeName());
                    showWinningResultDTO.setPrizeTier(ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier()));
                    showWinningResultDTO.setWinningTime(winningRecordDO.getWinningTime());
                    return showWinningResultDTO;
                })
                .collect(Collectors.toList());
        return showWinningResultDTOList;
    }

    /**
     * 保存中奖记录（数据库 + redis）
     * 查询 activity、user、activity_prize、prize 表
     * @param param
     * @return
     */
    @Override
    public List<WinningRecordDO> saveWinnerRecords(DrawPrizeParam param) {
        // 查询活动相关信息
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        // 查询活动奖品相关信息
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(),
                param.getPrizeId());
        // 查询奖品相关信息
        PrizeDO prizeDO = prizeMapper.selectById(param.getPrizeId());
        // 查询用户列表
        List<Long> userIds = param.getWinnerList().stream()
                .map(DrawPrizeParam.Winner::getUserId)
                .collect(Collectors.toList());
        List<UserDO> userDOList = userMapper.selectByIds(userIds);
        // 构造请求
        List<WinningRecordDO> winningRecordDOList = userDOList.stream()
                .map(userDO -> {
                    WinningRecordDO winningRecordDO = new WinningRecordDO();
                    // 活动 id 和 名称
                    winningRecordDO.setActivityId(activityDO.getId());
                    winningRecordDO.setActivityName(activityDO.getActivityName());
                    // 奖品 id、名称 和 等级
                    winningRecordDO.setPrizeId(prizeDO.getId());
                    winningRecordDO.setPrizeName(prizeDO.getName());
                    winningRecordDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
                    // 获奖人员 id、姓名、邮箱 和 电话
                    winningRecordDO.setWinnerId(userDO.getId());
                    winningRecordDO.setWinnerName(userDO.getUserName());
                    winningRecordDO.setWinnerEmail(userDO.getEmail());
                    winningRecordDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
                    // 获奖时间
                    winningRecordDO.setWinningTime(param.getWinningTime());
                    return winningRecordDO;
                })
                .collect(Collectors.toList());
        // 插入数据
        winnerRecordMapper.batchInsert(winningRecordDOList);

        // 缓存中奖者记录
        // 1. 缓存奖品维度中奖记录
        cacheWinningRecords(param.getActivityId() + "_" + param.getPrizeId(),
                winningRecordDOList, WINNING_RECORDS_TIMEOUT);
        // 2. 缓存活动维度中奖记录
        if (ActivityStatusEnum.COMPLETED.name().equalsIgnoreCase(activityDO.getStatus())) {
            // 查询所有中奖记录
            List<WinningRecordDO> allWinnerRecords = winnerRecordMapper.selectByActivityId(param.getActivityId());
            cacheWinningRecords(param.getActivityId().toString(),
                    allWinnerRecords, WINNING_RECORDS_TIMEOUT);
        }
        return winningRecordDOList;
    }

    /**
     * 将数据缓存到 redis 中
     * @param key
     * @param winningRecordDOList
     * @param timeout
     */
    private void cacheWinningRecords(String key, List<WinningRecordDO> winningRecordDOList, Long timeout) {
        // 参数校验
        if (!StringUtils.hasText(key)
                || CollectionUtils.isEmpty(winningRecordDOList)) {
            log.warn("需要缓存的 key {} 或 value {} 为空", key, winningRecordDOList);
            return;
        }
        // 缓存
        try {
            redisUtil.set(WINNING_RECORDS_PREFIX + key,
                    JacksonUtil.writeValueAsString(winningRecordDOList),
                    timeout);
        } catch (Exception e) {
            log.warn("redis 缓存 key:{} 和 value:{} 失败",
                    key, JacksonUtil.writeValueAsString(winningRecordDOList), e);
        }
    }

    /**
     * 将中奖信息发送到 rabbitMQ, 进行异步处理
     * @param param
     */
    @Override
    public void drawPrize(DrawPrizeParam param) {
        // 中奖相关信息
        String messageId = UUID.randomUUID().toString().replace("-", "");
        String messageData = JacksonUtil.writeValueAsString(param);
        // 发送消息
        Map<String, String> messageMap = new HashMap<>();
        messageMap.put(MESSAGE_ID, messageId);
        messageMap.put(MESSAGE_DATA, messageData);
        rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING, messageMap);
        log.info("drawPrize 向 mq 发送消息成功, map: {}", JacksonUtil.writeValueAsString(messageMap));
    }
}
